package com.ckx.cmd;

import jakarta.annotation.PostConstruct;
import lombok.extern.slf4j.Slf4j;
import org.apache.http.HttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpUriRequest;
import org.apache.http.concurrent.FutureCallback;
import org.apache.http.impl.nio.client.CloseableHttpAsyncClient;
import org.apache.http.impl.nio.client.HttpAsyncClients;
import org.apache.http.impl.nio.conn.PoolingNHttpClientConnectionManager;
import org.apache.http.impl.nio.reactor.DefaultConnectingIOReactor;
import org.apache.http.impl.nio.reactor.IOReactorConfig;
import org.apache.http.nio.reactor.IOReactorException;
import org.apache.http.util.EntityUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.stereotype.Component;
import org.springframework.util.StopWatch;

import java.util.concurrent.CountDownLatch;

@Slf4j
@Component
@RefreshScope
public class HttpClientCmd {

    private CloseableHttpAsyncClient httpAsyncClient;

    @Value("${press.url}")
    private String pressUrl;

    @PostConstruct
    public void init() throws IOReactorException {

        boolean pipelining = false;

        if (!pipelining) {
            httpAsyncClient = HttpAsyncClients.custom()
                    .setMaxConnPerRoute(600)
                    .build();
        } else {
            DefaultConnectingIOReactor ioReactor = new DefaultConnectingIOReactor(IOReactorConfig.DEFAULT);
            PoolingNHttpClientConnectionManager connManager = new PoolingNHttpClientConnectionManager(ioReactor);
            connManager.setDefaultMaxPerRoute(650);
            httpAsyncClient = HttpAsyncClients.createPipelining(connManager);
        }

        this.httpAsyncClient.start();
    }

    public void run(String... args) throws Exception {

        StopWatch stopWatch = new StopWatch();
        stopWatch.start();


        int size = 20000;
        final HttpGet httpget = new HttpGet(pressUrl);
        CountDownLatch latch = new CountDownLatch(size);
        HttpFutureCallback callback = new HttpFutureCallback(latch);

        for (int i = 0; i < size; i++) {
            doAsyncRequest(httpget, callback);
        }

        latch.await();
        stopWatch.stop();
        log.info("cost: {}", stopWatch.getTotalTimeSeconds());
    }

    private void doAsyncRequest(HttpUriRequest request, HttpFutureCallback callback) {
        httpAsyncClient.execute(request, callback);
    }

    public static class HttpFutureCallback implements FutureCallback<HttpResponse> {

        private final CountDownLatch latch;

        public HttpFutureCallback(CountDownLatch latch) {
            this.latch = latch;
        }

        private void done() {
            this.latch.countDown();
        }

        @Override
        public void completed(HttpResponse response) {
            try {
                done();
                String result = EntityUtils.toString(response.getEntity());
//                if (counter.get() % 10000 == 0) {
//                    log.info("result len: {}, cost: {}ms", result.length(), System.currentTimeMillis() - startTime);
//                }
            } catch (Exception e) {
                log.error("xxx", e);
            }
        }

        @Override
        public void failed(Exception ex) {
            done();
        }

        @Override
        public void cancelled() {
            done();
        }

    }
}
